/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufba.lasid.ds.replication;

import br.ufba.lasid.base.api.IResource;
import br.ufba.lasid.comm.api.ICommunicator;
import br.ufba.lasid.comm.api.IHost;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceManager;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceManager.ModuleTypes;
import br.ufba.lasid.ds.replication.api.IReplicatedResourcePeriodicDetectorModule;
import br.ufba.lasid.utils.ReplicaRole;
import br.ufba.lasid.utils.StateXen;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author aliriosa
 */
public class ReplicatedResourcePeriodicDetectorModule implements IReplicatedResourcePeriodicDetectorModule {

    private IReplicatedResourceManager replicator;
    private long period = 100L;
    private static final Log logger = LogFactory.getLog(ReplicatedResourceManager.class);
    private ICommunicator communicator;
    private ReplicaRole style;
    private boolean running;

    public ReplicatedResourcePeriodicDetectorModule(IReplicatedResourceManager replicator) {
        this.replicator = replicator;
    }

    public ReplicatedResourcePeriodicDetectorModule() {
    }

    @Override
    public String getName() throws Exception {
        return this.getModuleType().name();
    }

    @Override
    public ModuleTypes getModuleType() throws Exception {
        return ModuleTypes.PeriodicDetector;
    }

    @Override
    public void setReplicatedResource(IReplicatedResourceManager replicator) throws Exception {
        this.replicator = replicator;
    }

    @Override
    public synchronized void setPeriod(long period) {
        this.period = period;
    }

    @Override
    public synchronized long getPeriod() {
        return this.period;
    }

    @Override
    public void startup() throws Exception {
        new Thread(this).start();
    }

    @Override
    public void run() {
        try {
            communicator = replicator.getCommunicator();
            style = replicator.getResource().getRole();

            while (true) {
                try {
                    Thread.sleep(getPeriod());
                    perform();
                } catch (Exception ex) {
                    logger.error("A runtime error has been detected.", ex);
                }
            }
        } catch (Exception ex) {
            logger.error("couldn't start", ex);
        }
    }

    @Override
    public void perform() throws Exception {
        Thread.sleep(5000L);
        if (ReplicaRole.Secondary.equals(replicator.getResource().getRole())) {
            if (this.replicator.getDetector().getRemotehost().isDown()) {
                logger.debug("Setting to Primary ...");

                changeResourceToPrimary();
                makeRestore();
                replicator.getResource().setRole(ReplicaRole.Primary);
            }
        } else if (ReplicaRole.Primary.equals(replicator.getResource().getRole())) {
            if (this.replicator.getDetector().getRemotehost().isDown()) {
                // logger.debug("Setting to Secondary ...");
                // changeResourceToSecondary();
                // this.style = IReplicatedResourceManager.ReplicaRole.Backup;
            }
        }
    }

    @Override
    public void changeResourceToPrimary() throws IOException, InterruptedException {
        logger.debug("Changing to primary ... ");
        String exe = "drbdadm primary " + replicator.getResource().getResource();
        logger.info(exe);
        Runtime.getRuntime().exec(exe);
        Thread.sleep(2000L);

        exe = "mount " + replicator.getResource().getDev() + " " + replicator.getResource().getPath();
        logger.info(exe);
        Runtime.getRuntime().exec(exe);

        boolean flag = true;
        while (flag) {
            Thread.sleep(2000L);
            exe = "df | grep " + replicator.getResource().getDev();
            logger.info(exe);
            Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", exe});
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String pOut = in.readLine();
            if (StringUtils.contains(pOut, replicator.getResource().getDev())) {
                logger.info("pOut = " + pOut);
                flag = false;
                Thread.sleep(1000L);
            }
        }
        logger.debug("... success");
    }

    @Override
    public void makeRestore() throws IOException, InterruptedException {

        logger.debug("Making restore ... ");

        String last = replicator.getLastCheckPoint();
        if (StringUtils.isNotEmpty(last)) {
            String exe = "xm restore " + last;
            logger.info(exe);
            Runtime.getRuntime().exec(exe);

            boolean flag = true;
            while (flag) {
                Thread.sleep(5000L);
                if (StateXen.IDLE.equals(replicator.getVm().getStateByName())) {
                    flag = false;
                }
            }
            logger.debug("Success restore ... ");
        } else {
            logger.debug("ERROR. Last checkPoint was not find!");
        }
    }

    @Override
    public void changeResourceToSecondary() throws IOException, InterruptedException {
        logger.debug("Changing to secondary ... ");

        String exe = "umount -l " + replicator.getResource().getPath();
        logger.info(exe);
        Runtime.getRuntime().exec(exe);
        Thread.sleep(10000L);

        exe = "drbdadm secondary " + replicator.getResource().getResource();
        logger.info(exe);
        Runtime.getRuntime().exec(exe);
        Thread.sleep(5000L);

//        exe = "drbdadm -- --discard-my-data connect " + replicator.getResource().getResource();
//        System.out.println(exe);
//        Runtime.getRuntime().exec(exe);
//        Thread.sleep(10000L);
        logger.debug("... success");
    }

    public void scheduleTimeout(IHost host) throws Exception {
        String hostID = host.getID();
    }

    public void cancelTimeout(IHost host) throws Exception {
        String hostID = host.getID();
    }

    class Sender extends Thread {

        IHost host;
        String message;

        public Sender() {
        }

        public Sender(IHost host, String message) {
            this.host = host;
            this.message = message;
        }

        @Override
        public void run() {
            String hostID = host.getID();
            try {
                communicator.send(hostID, replicator.getName(), message);
            } catch (ConnectException ce) {
                host.setState(IResource.States.Down);
                //ce.printStackTrace();
            } catch (NoRouteToHostException nre) {
                host.setState(IResource.States.Down);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
